package com.tianhao.luo.springbusiness.service.impl;

import com.tianhao.luo.springbusiness.dao.UserDao;
import com.tianhao.luo.springbusiness.entity.User;
import com.tianhao.luo.springbusiness.service.OrderService;
import com.tianhao.luo.springbusiness.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author tianhao.luo@hand-china.com
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private OrderService orderService;

    @Autowired
    @SuppressWarnings("ALL")
    private UserDao userDao;

    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testPropagationOfRequired() {
        // REQUIRED:如果当前没有事务则创建事务，有就直接加入该事务；
        // 特点是：不一定要创建事务
        // 如果orderService.testPropagationOfRequired();方法抛出异常被回滚，则整个事务都会回滚
        User user = new User();
        user.setName("testPropagationOfRequired");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        // 1 orderService方法事务抛出异常，并且userService不做处理，也等同于抛出异常，发现user和order的数据都回滚了
        orderService.testPropagationOfRequired();
        // 2 捕获orderService方法事务抛出的异常，并且userService不抛出，发现user和order的数据都回滚了
        //try {
        //    orderService.testPropagationOfRequired();
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}


    }

    @Override
    //@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    public void testPropagationOfSupports() {
        // SUPPORTS：支持当前事务，如果当前事务不存在，那么本方法也不进行事务控制
        // 简言之，如果当前是有事务的，则加入该事务；如果没有事务，则本方法也不创建事务

        // 本测试方法上加了SUPPORTS，执行的结果应该是，user和order表都插入数据了，没有回滚
        // 本测试方法上改成REQUIRED，执行的结果应该是，user和order表都插入数据了，且发生了回滚
        User user = new User();
        user.setName("testPropagationOfSupports");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        orderService.testPropagationOfSupports();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    //@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
    public void testPropagationOfMandatory() {
        // 如果当前有事务则加入，如果没有事务则抛出异常
        // 体现了它的名字：强制

        User user = new User();
        user.setName("testPropagationOfMandatory");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        orderService.testPropagationOfMandatory();

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void testPropagationOfRequiresNew() {
        // 创建一个新事务，如果当前有事务，就暂停当前事务，而使用这个新事务
        // 假设当前有两个事务T1和T2，T2是新事务，T1是原来的事务；
        // 1⃣️ 如果T1（原来的事务）发生异常回滚，T2（新事务）没有出现异常，T2（新事务）不会回滚数据；
        // 如果T2（新事务）发生异常回滚，并抛出异常给T1（原来的事务）：
        // 2⃣️ T1（原来的事务）如果捕获了新事务的异常并不抛出时，那么T1（原来的事务）不会发生回滚；
        // 3⃣️ T1（原来的事务）不捕获异常或捕获了但是也抛出该异常，那么T1（原来的事务）会跟着回滚

        User user = new User();
        user.setName("testPropagationOfRequiresNew");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        // 上面的2⃣️和3⃣️的情况都交给orderService来测试

        // 1 测试上面的2⃣️情况，捕获新事务的异常
        //try {
        //    orderService.testPropagationOfRequiresNew();
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}

        // 2 测试上面的3⃣️情况，不捕获新事务的异常，原来的事务也抛出该异常
        //orderService.testPropagationOfRequiresNew();

        // 3 测试上面的1⃣️情况，此处抛出异常
        orderService.testPropagationOfRequiresNew3();
        throw new RuntimeException("原来的事务抛出异常");

        // 新事务中的异常被新事务方法捕获了，那么原来的事务和新事务的数据都不会回滚
        //orderService.testPropagationOfRequiresNew2();
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
    //@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testPropagationOfNotSupport() {
        // 执行本方法不开启事务，如果当前有事务，那么会暂停当前的事务
        // NOT_SUPPORTED作用就是本方法不开启事务
        User user = new User();
        user.setName("testPropagationOfNotSupport");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        orderService.testPropagationOfNotSupport();

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    //@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
    public void testPropagationOfNever() {
        // 如果当前存在事务，就会直接抛出异常；本方法不会创建事务
        User user = new User();
        user.setName("testPropagationOfNever");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        orderService.testPropagationOfNever();

    }

    @Override
    @Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class,isolation = Isolation.DEFAULT)
    public void testPropagationOfNested() {
        // 如果当前有事务，就创建事务嵌套进入当前事务；如果没有事务，就创建一个事务
        // 和REQUIRED的区别在于，此处一定会创建事务，如果当前有事务，就作为该事务的嵌套

        User user = new User();
        user.setName("testPropagationOfNested");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");

        // orderService中两个方法的传播机制也是NESTED，并且testPropagationOfNested2中不会出错，testPropagationOfNested会抛出错误
        // 此时相当于有userService的事务嵌套了两个子事务，这两个子事务A和B，A先执行成功，B执行时抛出错误
        orderService.testPropagationOfNested2();
        try {
            orderService.testPropagationOfNested();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // orderService方法的传播机制也是NESTED，并且它执行成功，但是userService执行失败，user和order的数据都会回滚
        //orderService.testPropagationOfNested2();
        //throw new RuntimeException("当前的事务异常");
    }


    @Override
    //@Transactional(isolation = Isolation.READ_UNCOMMITTED,rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    //@Transactional(isolation = Isolation.READ_COMMITTED,rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    //@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    @Transactional(isolation = Isolation.SERIALIZABLE,rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void testDirtyRead() {
        // 隔离级别是针对事务与事务之间的，而且是针对同一张表的
        // 所以这里使用的传播机制是REQUIRED_NEW,保证每一个方法都是新事务

        // 下面是模拟脏读的操作
        // 1 user正常插入数据
        // 2 orderService利用这个还没有提交的数据做了自己的操作
        // 3 userService最后还抛出了异常
        // 由于这里的传播机制是REQUIRES_NEW，所以userService和orderService是两个事务，userService的异常不会造成orderService的回滚

        User user = new User();
        user.setName("testDirtyRead");
        userDao.insertSelective(user);
        System.out.println("===UserService插入成功===");
        orderService.testDirtyRead(user);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.READ_UNCOMMITTED)
    public void testLostEdit(User user) {
        // 丢失修改，是指同时有多个事务操作同一个数据，先提交的事务的修改被后来的事务覆盖了
        User temp = userDao.selectByPrimaryKey(user.getId());
        temp.setName("userService");
        userDao.updateByPrimaryKey(temp);
        System.out.println("====userService修改成功：userService====");
    }

    @Override
    public User initUserData() {
        User user = new User();
        user.setName("initUserData");
        userDao.insertSelective(user);
        System.out.println("===基础数据，UserService插入成功===");
        return user;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.READ_UNCOMMITTED)
    //@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.SERIALIZABLE)
    //@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.REPEATABLE_READ)
    //@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    public void testNotRepeatableRead(User user) {
        // 1 第一次读取数据
        User temp = userDao.selectByPrimaryKey(user.getId());
        System.out.println("userService第一次读取："+temp.toString());

        // 2 其他事务修改该数据
        orderService.testNotRepeatableRead(user);

        // 3 该事务继续读取一次数据
        User temp2 = userDao.selectByPrimaryKey(user.getId());
        System.out.println("userService第二次读取："+temp2.toString());
    }

    @Override
    //@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.READ_UNCOMMITTED)
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.SERIALIZABLE)
    //@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.REPEATABLE_READ)
    //@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    public void testPhantomRead() {
        // 1 第一次读取数据
        List<User> users = userDao.selectAll();
        System.out.println("userService第一次读取："+users.toString());
        // 2 其他事务插入新数据
        orderService.testPhantomRead();
        // 3 第二次读取数据
        List<User> users2 = userDao.selectAll();
        System.out.println("userService第二次读取："+users2.toString());
    }

}
